// Tierless
// Copyright © 2010 Alphora
// This file is licensed under a modified BSD-license (see tierless_license.txt).
using System.Linq;
using System.Runtime.Serialization;

namespace Tierless.Framework.Data
{
	/// <summary>
	/// Represents an entity traversal, a shorthand for describing related entities to be included in an entity request.
	/// </summary>
	[DataContract]
	public class EntityTraversal
	{
		/// <summary>
		/// Initializes a new instance of an EntityTraversal.
		/// </summary>
		/// <param name="traversalType">The type of the entity traversal, immediate or recursive.</param>
		/// <param name="relatedEntityTypeNames">The names of related entities.</param>
		public EntityTraversal(EntityTraversalType traversalType, params string[] relatedEntityTypeNames)
		{
			TraversalType = traversalType;
			RelatedEntityTypeNames = relatedEntityTypeNames;
		}

		/// <summary>
		/// Initializes a new instance of an EntityTraversal.
		/// </summary>
		/// <param name="traversalType">The type of the entity traversal, immediate or recursive.</param>
		public EntityTraversal(EntityTraversalType traversalType)
			: this(traversalType, null)
		{}

		/// <summary>
		/// Gets the type of the traversal.
		/// </summary>
		[DataMember]
		public EntityTraversalType TraversalType { get; private set; }
		
		/// <summary>
		/// Gets the related entity type names for the traversal.
		/// </summary>
		[DataMember]
		public string[] RelatedEntityTypeNames { get; private set; }

		/// <summary>
		/// Returns true if the given traversal is equal, by value, to this traversal.
		/// </summary>
		/// <param name="obj">The traversal to be tested.</param>
		/// <returns>True if the given traversal is equal, by value, to this traversal, false otherwise.</returns>
		/// <remarks>
		/// Two entity traversal instances are equal if they describe the same entity traversal. In other words,
		/// if they have the same entity traversal type, and the same set of related entity type names.
		/// </remarks>
		public override bool Equals(object obj)
		{
			var traversal = obj as EntityTraversal;
			return (traversal != null)
				&& (TraversalType == traversal.TraversalType)
					&& ((RelatedEntityTypeNames == null) == (traversal.RelatedEntityTypeNames == null))
						&&
						(
							(RelatedEntityTypeNames == null)
								||
								(
									(RelatedEntityTypeNames.Length == traversal.RelatedEntityTypeNames.Length)
										&&
										RelatedEntityTypeNames.All
											(
												AValue => traversal.RelatedEntityTypeNames.Contains(AValue)
											)
									)
							);
		}

		public override int GetHashCode()
		{
			var namesHash = 0;
			if (RelatedEntityTypeNames != null)
				namesHash = RelatedEntityTypeNames.Aggregate(namesHash, (ACurrent, AName) => ACurrent ^ ACurrent);

			return TraversalType.GetHashCode() ^ namesHash;
		}
	}

	/// <summary>
	/// Represents the possible entity traversal types.
	/// </summary>
	public enum EntityTraversalType
	{
		/// <summary>
		/// Indicates that only entities that are immediately related to the main entity will be traversed.
		/// </summary>
		Immediate,

		/// <summary>
		/// Indicates that all entities will be traversed, recursively.
		/// </summary>
		Recursive
	};
}